{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CI/CD for a Kubeflow pipeline on Vertex AI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Learning Objectives:**\n",
    "1. Learn how to create a custom Cloud Build builder to pilote Vertex AI Pipelines\n",
    "1. Learn how to write a Cloud Build config file to build and push all the artifacts for a KFP\n",
    "1. Learn how to setup a Cloud Build GitHub trigger a new run of the Kubeflow PIpeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this lab you will walk through authoring of a **Cloud Build** CI/CD workflow that automatically builds, deploys, and runs a Kubeflow pipeline on Vertex AI. You will also integrate your workflow with **GitHub** by setting up a trigger that starts the  workflow when a new tag is applied to the **GitHub** repo hosting the pipeline's code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuring environment settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "PROJECT_ID = !(gcloud config get-value project)\n",
    "PROJECT_ID = PROJECT_ID[0]\n",
    "REGION = 'us-central1'\n",
    "ARTIFACT_STORE = f'gs://{PROJECT_ID}-vertex'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us make sure that the artifact store exists:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!gsutil ls | grep ^{ARTIFACT_STORE}/$ || gsutil mb -l {REGION} {ARTIFACT_STORE}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the KFP CLI builder for Vertex AI\n",
    "### Review the Dockerfile describing the KFP CLI builder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat kfp-cli_vertex/Dockerfile"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Build the image and push it to your project's **Container Registry**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "KFP_CLI_IMAGE_NAME = 'kfp-cli-vertex'\n",
    "KFP_CLI_IMAGE_URI = f'gcr.io/{PROJECT_ID}/{KFP_CLI_IMAGE_NAME}:latest'\n",
    "KFP_CLI_IMAGE_URI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!gcloud builds submit --timeout 15m --tag {KFP_CLI_IMAGE_URI} kfp-cli_vertex"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Understanding the **Cloud Build** workflow.\n",
    "\n",
    "Review the `cloudbuild_vertex.yaml` file to understand how the CI/CD workflow is implemented and how environment specific settings are abstracted using **Cloud Build** variables.\n",
    "\n",
    "The CI/CD workflow automates the steps you walked through manually during `lab-02_vertex`:\n",
    "1. Builds the trainer image\n",
    "1. Compiles the pipeline\n",
    "1. Uploads and run the pipeline to the Vertex AI Pipeline environment\n",
    "1. Pushes the trainer to your project's **Container Registry**\n",
    " \n",
    "\n",
    "The **Cloud Build** workflow configuration uses both standard and custom [Cloud Build builders](https://cloud.google.com/cloud-build/docs/cloud-builders). The custom builder encapsulates **KFP CLI**. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Manually triggering CI/CD runs\n",
    "\n",
    "You can manually trigger **Cloud Build** runs using the [gcloud builds submit command]( https://cloud.google.com/sdk/gcloud/reference/builds/submit)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SUBSTITUTIONS= f'_REGION={REGION}'\n",
    "SUBSTITUTIONS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!gcloud builds submit . --config cloudbuild_vertex.yaml --substitutions {SUBSTITUTIONS}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** If you experience issues with CloudBuild being able to access Vertex AI, you may need to run the following commands in **CloudShell**:\n",
    "\n",
    "```\n",
    "PROJECT_ID=$(gcloud config get-value project)\n",
    "PROJECT_NUMBER=$(gcloud projects list --filter=\"name=$PROJECT_ID\" --format=\"value(PROJECT_NUMBER)\")\n",
    "gcloud projects add-iam-policy-binding $PROJECT_ID \\\n",
    "    --member=\"serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com\" \\\n",
    "    --role=\"roles/aiplatform.user\"\n",
    "gcloud iam service-accounts add-iam-policy-binding \\\n",
    "    $PROJECT_NUMBER-compute@developer.gserviceaccount.com \\\n",
    "    --member=\"serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com\" \\\n",
    "    --role=\"roles/iam.serviceAccountUser\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting up GitHub integration\n",
    "\n",
    "In this exercise you integrate your CI/CD workflow with **GitHub**, using [Cloud Build GitHub App](https://github.com/marketplace/google-cloud-build). \n",
    "You will set up a trigger that starts the CI/CD workflow when a new tag is applied to the **GitHub** repo managing the  pipeline source code. You will use a fork of this repo as your source GitHub repository."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a fork of this repo\n",
    "[Follow the GitHub documentation](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) to fork this repo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a **Cloud Build** trigger\n",
    "\n",
    "Connect the fork you created in the previous step to your Google Cloud project and create a trigger following the steps in the [Creating GitHub app trigger](https://cloud.google.com/cloud-build/docs/create-github-app-triggers) article. Use the following values on the **Edit trigger** form:\n",
    "\n",
    "|Field|Value|\n",
    "|-----|-----|\n",
    "|Name|[YOUR TRIGGER NAME]|\n",
    "|Description|[YOUR TRIGGER DESCRIPTION]|\n",
    "|Event| Tag|\n",
    "|Source| [YOUR FORK]|\n",
    "|Tag (regex)|.\\*|\n",
    "|Build Configuration|Cloud Build configuration file (yaml or json)|\n",
    "|Cloud Build configuration file location| ./immersion/kubeflow_pipelines/cicd/solutions/cloudbuild_vertex.yaml|\n",
    "\n",
    "\n",
    "Use the following values for the substitution variables:\n",
    "\n",
    "|Variable|Value|\n",
    "|--------|-----|\n",
    "|_REGION|us-central1|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Trigger the build\n",
    "\n",
    "To start an automated build [create a new release of the repo in GitHub](https://help.github.com/en/github/administering-a-repository/creating-releases). Alternatively, you can start the build by applying a tag using `git`. \n",
    "```\n",
    "git tag [TAG NAME]\n",
    "git push origin --tags\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After running the command above, a build should have been automatically triggered, which you should able to inspect [here](https://console.cloud.google.com/cloud-build/builds)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2021 Google LLC\n",
    "\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "you may not use this file except in compliance with the License.\n",
    "You may obtain a copy of the License at\n",
    "\n",
    "    https://www.apache.org/licenses/LICENSE-2.0\n",
    "\n",
    "Unless required by applicable law or agreed to in writing, software\n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "See the License for the specific language governing permissions and\n",
    "limitations under the License."
   ]
  }
 ],
 "metadata": {
  "environment": {
   "name": "tf2-gpu.2-3.m75",
   "type": "gcloud",
   "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-3:m75"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
